<template>
  <div ref="canvas"></div>
</template>

<script lang="ts" setup>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import Stats from "three/examples/jsm/libs/stats.module";
import {GUI} from "three/examples/jsm/libs/lil-gui.module.min.js";
import { ref, onMounted } from "vue";

const canvas = ref<any>('');

const stat = Stats();

const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 3000);
camera.position.set(-30, 40, 30);
camera.lookAt(0, 0, 0);

const renderer = new THREE.WebGLRenderer({
  antialias: true,
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x000000);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

const orbitControl = new OrbitControls(camera, renderer.domElement);

// plane
const texture = new THREE.TextureLoader().load(new URL("@/assets/textures/ground/grasslight-big.jpg", import.meta.url).href);
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(10, 10);
const planeGeometry = new THREE.PlaneGeometry(1000, 1000, 20, 20);
const planeMaterial = new THREE.MeshLambertMaterial({
  map: texture,
  side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.position.set(15, 0, 0);
plane.rotateX(-0.5 * Math.PI);
scene.add(plane);

// cube
const cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
const cubeMaterial = new THREE.MeshLambertMaterial({
  color: 0xff3333,
});
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
cube.position.set(-4, 3, 0);
scene.add(cube);

// sphere
const sphereGeometry = new THREE.SphereGeometry(4, 25, 25);
const sphereMaterial = new THREE.MeshLambertMaterial({
  color: 0x7777ff
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.castShadow = true;
sphere.position.set(10, 5, 10);
scene.add(sphere);

// spot light
const spotLight = new THREE.SpotLight(0xcccccc);
spotLight.position.set(-40, 60, -10);
spotLight.lookAt(plane.position);
scene.add(spotLight);

// light target
const target = new THREE.Object3D();
target.position.set(5, 0, 0);

// hemi light
const hemiLight = new THREE.HemisphereLight(0x0000ff, 0x00ff00, 0.6);
hemiLight.position.set(0, 500, 0);
scene.add(hemiLight);

// directional light
const dirLight = new THREE.DirectionalLight("#ffffff");
dirLight.position.set(30, 10, -50);
dirLight.castShadow = true;
dirLight.target = plane;
dirLight.shadow.camera.near = 0.1;
dirLight.shadow.camera.far = 1000;
dirLight.shadow.camera.left = -100;
dirLight.shadow.camera.right = 100;
dirLight.shadow.camera.top = 100;
dirLight.shadow.camera.bottom = -100;
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
scene.add(dirLight);

// gui
let step = 0;
const controlObj = {
  rotationSpeed : 0.02,
  bouncingSpeed : 0.02,
  hemisphere : true,
  skyColor : 0x0000ff,
  groundColor : 0x00ff00,
  intensity : 0.6,
};
const gui = new GUI();
gui.addColor(controlObj, 'groundColor').name("半球光光源地面颜色").onChange((e) => {
  hemiLight.groundColor = new THREE.Color(e);
});
gui.addColor(controlObj, 'skyColor').name("半球光光源天空颜色").onChange((e) => {
  hemiLight.groundColor = new THREE.Color(e);
});
// @ts-ignore
gui.add(controlObj, 'hemisphere').name("启用半球光光源").onChange(function (e) {
  if (!e) {
    hemiLight.intensity = 0;
  } else {
    hemiLight.intensity = controlObj.intensity;
  }
});
// @ts-ignore
gui.add(controlObj, 'intensity', 0, 5).name("半球光光源强度").onChange(function (e) {
  hemiLight.intensity = e;
});

const render = () => {
  stat.update();
  orbitControl.update();

  cube.rotation.x += controlObj.rotationSpeed;
  cube.rotation.y += controlObj.rotationSpeed;
  cube.rotation.z += controlObj.rotationSpeed;

  step += controlObj.bouncingSpeed;
  sphere.position.x = 20 + (10 * (Math.cos(step)));
  sphere.position.y = 2 + (10 * Math.abs(Math.sin(step)));

  requestAnimationFrame(render);
  renderer.render(scene, camera);
};

onMounted(() => {
  canvas.value.appendChild(stat.domElement);
  canvas.value.appendChild(renderer.domElement);
  render();
})

</script>